﻿/*SOWY Date Operations class
 * Author: WANG JIAN
 * Group: f10-2s
 * Version 0.01
 * This class is to deal mainly with the text based database
 * Basic function include adding of data, retrieval of data, modify and deleting of data.
 * This class may include ability to record a log of user input history in the future
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using SOWY.common;

namespace SOWY
{
    class DataOperations
    {
        private string fileLoc;

        #region Database initialization
        public DataOperations(string fileLocation = GeneralConstant.DATABASE_FOLDER+GeneralConstant.DATABASE_FILE)
        {
            fileLoc = fileLocation;
            if (!isFolderExist(GeneralConstant.DATABASE_FOLDER))
            {
                Directory.CreateDirectory(GeneralConstant.DATABASE_FOLDER);
            }
            if (!isFileExist(fileLoc))
            {
                using (FileStream fs = File.Create(fileLoc, 1024))
                {
                    Byte[] info = new UTF8Encoding(true).GetBytes("");
                    fs.Write(info, 0, info.Length);
                    fs.Close();
                }
            }
        }
        //Return a list of task with updated status
        public List<Task> taskStatusInit()
        {
            List<Task> returnTasks = new List<Task>();
            List<Task> dataBase = readTasks();
            foreach (Task t in dataBase)
            {
                if (isTheSameDay(t.startDate, DateTime.Now) && isTheSameDay(t.startDate, DateTime.Now) && t.status != GeneralConstant.TASK_START)
                {
                    t.status = GeneralConstant.TASK_START;
                    returnTasks.Add(t);
                }
                if (isTheSameDay(t.endDate, DateTime.Now) && isTheSameDay(t.endDate, DateTime.Now) && t.status != GeneralConstant.TASK_COMPLETE)
                {
                    t.status = GeneralConstant.TASK_OVERDUE;
                    returnTasks.Add(t);
                }
            }
            return returnTasks;
        }
        #endregion

        #region Command processing
        public List<Task> toDatabase(List<Command> commands)
        {
            List<Task> dataBase = readTasks();
            List<Task> returnTasks = new List<Task>();
            foreach (Command currentCommand in commands)
            {
                if (currentCommand.CRUD == GeneralConstant.KEY_ADD)
                {
                    returnTasks = appendCompletedTasks(returnTasks,addTask(currentCommand.taskName, currentCommand.startDate, currentCommand.endDate, dataBase));
                }

                if (currentCommand.CRUD == GeneralConstant.KEY_RETRIEVE)
                {
                    returnTasks = appendCompletedTasks(returnTasks, displayTasks(dataBase));
                }

                if (currentCommand.CRUD == GeneralConstant.KEY_DELETE)
                {
                    returnTasks = appendCompletedTasks(returnTasks,deleteTask(currentCommand.taskName, currentCommand.startDate, currentCommand.endDate, dataBase));
                }

                if (currentCommand.CRUD == GeneralConstant.KEY_UPDATE)
                {
                    returnTasks = appendCompletedTasks(returnTasks, updateTask(currentCommand.taskName, currentCommand.startDate, currentCommand.endDate, dataBase, currentCommand.parameter, currentCommand.newValue));
                }
            }
            return returnTasks;
        }

        private List<Task> appendCompletedTasks(List<Task> oldList, List<Task> taskToAppend)
        {
            foreach (Task t in taskToAppend)
            {
                oldList.Add(t);
            }
            return oldList;
        }
        #endregion

        #region Command operations
        private static List<Task> displayTasks(List<Task> dataBase)
        {
            List<Task> returnTasks = dataBase;
            if (returnTasks.Count == GeneralConstant.EMPTY)
                returnTasks.Add(new Task(GeneralConstant.ERROR_EMPTY_DISPLAY, DateTime.MinValue, DateTime.MinValue, GeneralConstant.KEY_ERROR));
            else
            {
                returnTasks.Sort((x, y) => DateTime.Compare(x.startDate, y.startDate));
                returnTasks.Sort((x, y) => DateTime.Compare(x.endDate, y.endDate));
            }
            return returnTasks;
        }

        private List<Task> updateTask(string task, DateTime firstDate, DateTime secondDate, List<Task> dataBase, string[] parameterToChange, string[] newParameter)
        {
            int tasksToAdd = 1, changingParameter = 0;
            string newTaskName = null, newStatus = null, newStartDate = null, newEndDate = null;

            List<Task> returnTasks = new List<Task>();
            List<Task> deletedTasks = new List<Task>();

            foreach(string s in parameterToChange)
            {
                if (s== GeneralConstant.KEY_TASK)
                    newTaskName = newParameter[changingParameter++];
                else if (s == GeneralConstant.KEY_STATUS)
                    newStatus = newParameter[changingParameter++];
                else if (s == GeneralConstant.KEY_STARTDATE)
                    newStartDate = newParameter[changingParameter++];
                else if (s == GeneralConstant.KEY_ENDDATE)
                    newEndDate = newParameter[changingParameter++];
            }

            deletedTasks = deleteTask(task, firstDate, secondDate, dataBase);
            while ((deletedTasks.Count - tasksToAdd) >= 0)
            {
                returnTasks.Add(deletedTasks[deletedTasks.Count - tasksToAdd]);
                if (returnTasks.ElementAt(returnTasks.Count - 1).type == GeneralConstant.KEY_ERROR)
                {
                    returnTasks.ElementAt(returnTasks.Count - 1).task = GeneralConstant.ERROR_EMPTY_UPDATE;
                    break;
                }
                    returnTasks.Add(addTask(valueChanged(returnTasks.ElementAt(returnTasks.Count - 1).task, newTaskName) ,
                        Convert.ToDateTime(valueChanged(returnTasks.ElementAt(returnTasks.Count - 1).startDate.ToString(), newStartDate)),
                        Convert.ToDateTime(valueChanged(returnTasks.ElementAt(returnTasks.Count - 1).endDate.ToString(), newEndDate)),
                        dataBase, valueChanged(returnTasks.ElementAt(returnTasks.Count - 1).status, newStatus))[GeneralConstant.FIRST_ITEM]);
                tasksToAdd++;
            }

            if (returnTasks.Count == GeneralConstant.EMPTY)
                returnTasks.Add(new Task(GeneralConstant.ERROR_EMPTY_UPDATE, DateTime.MinValue, DateTime.MinValue, GeneralConstant.KEY_ERROR));
            return returnTasks;
        }
        private List<Task> deleteTask(string task, DateTime firstDate, DateTime secondDate, List<Task> dataBase)
        {
            int allDelete = GeneralConstant.FIRST_ITEM;
            int amountToDelete = GeneralConstant.FIRST_ITEM;
            String[] split = new String[] { };
            List<Task> returnTasks = new List<Task>();
            List<int> deleteTasks = new List<int>();
            int startCount, endCount = GeneralConstant.FIRST_ITEM;

            if (task == GeneralConstant.KEY_ALL && secondDate == DateTime.MinValue)
                while ((dataBase.Count - allDelete) > 0) deleteTasks.Add(allDelete++);
            else
                deleteTasks = searchTasks(dataBase, task, firstDate, secondDate);
            startCount = deleteTasks.Count;

            if (startCount == GeneralConstant.EMPTY)
                returnTasks.Add(new Task(GeneralConstant.ERROR_EMPTY_DELETE, DateTime.MinValue, DateTime.MinValue, GeneralConstant.KEY_ERROR));
            else
            {
                if (task != null && task != GeneralConstant.KEY_ALL && task.Contains("|"))
                {
                    split = task.Split('|');
                    amountToDelete = int.Parse(split[1]);
                    if (split[GeneralConstant.FIRST_ITEM] == GeneralConstant.KEY_FIRST)
                    {
                        if ((deleteTasks.Count.CompareTo(amountToDelete)) < 0)
                            startCount = deleteTasks.Count;
                        else
                            startCount = amountToDelete;

                        endCount = GeneralConstant.FIRST_ITEM;
                    }
                    else if (split[GeneralConstant.FIRST_ITEM] == GeneralConstant.KEY_LAST)
                    {
                        startCount = deleteTasks.Count;
                        endCount = deleteTasks.Count - amountToDelete - 1;
                    }
                }

                for (int i = startCount - 1; i >= endCount && i >= GeneralConstant.FIRST_ITEM; i--)
                {
                    int fileTask = deleteTasks.ElementAt(i);
                    //if (task == GeneralConstant.KEY_ALL || isTheSameDay(dataBase.ElementAt(fileTask).startDate, firstDate) && isTheSameDay(dataBase.ElementAt(fileTask).endDate, secondDate))
                    //if (task == GeneralConstant.KEY_ALL || isTheSameDay(dataBase.ElementAt(fileTask).endDate, secondDate))
                    {
                        dataBase.ElementAt(fileTask).type = GeneralConstant.KEY_DELETE;
                        returnTasks.Add(dataBase.ElementAt(fileTask));
                        dataBase.RemoveAt(fileTask);
                    }
                }
                writeTasks(dataBase);
            }
            return returnTasks;
        }

        private List<Task> addTask(string task, DateTime firstDate, DateTime secondDate, List<Task> dataBase, string stat = null)
        {
            List<Task> returnTasks = new List<Task>();

            Task taskToAdd = new Task(task, firstDate, secondDate, GeneralConstant.KEY_ADD, stat);
            if (taskToAdd.status == null)
            {
                if (firstDate > DateTime.Now)
                    taskToAdd.status = GeneralConstant.TASK_PENDING;
                else
                    taskToAdd.status = GeneralConstant.TASK_START;
            }
            else
                taskToAdd.status = stat;

            if (listContainTask(taskToAdd, dataBase))
            {
                taskToAdd.task = GeneralConstant.ERROR_EXISTING_ENTRY;
                taskToAdd.type = GeneralConstant.KEY_ERROR;
                returnTasks.Add(taskToAdd);
            }
            else
            {
                dataBase.Add(taskToAdd);
                if (writeTasks(dataBase))
                    returnTasks.Add(taskToAdd);
                else
                {
                    taskToAdd.type = GeneralConstant.KEY_ERROR;
                    returnTasks.Add(taskToAdd);
                }
            }
            return returnTasks;
        }


        private List<int> searchTasks(List<Task> textLines, string job, DateTime searchStartDate, DateTime searchEndDate)
        {
            if (searchStartDate == DateTime.MinValue)
                searchStartDate = searchEndDate;
            string[] split = job.Split(' ');
            string allJob = job.Replace(split[GeneralConstant.FIRST_ITEM], "").Trim();
            List<int> searchedStrings = new List<int>();
            for (int noOfLines = GeneralConstant.FIRST_ITEM; noOfLines < textLines.Count; noOfLines++)
            {
                if (textLines[noOfLines].task == allJob && split[GeneralConstant.FIRST_ITEM] == GeneralConstant.KEY_ALL ||//All of certain task
                    job == GeneralConstant.KEY_ALL && isTheSameDay(textLines[noOfLines].endDate, searchEndDate))//All task that day
                    searchedStrings.Add(noOfLines);
                else if (searchEndDate == DateTime.MinValue)//Any task with same name without endDate
                {
                    if (textLines[noOfLines].task == job && textLines[noOfLines].endDate ==searchEndDate)//Floating task only
                        searchedStrings.Add(noOfLines);
                }
                else if (searchStartDate == DateTime.MinValue)//Any task with same endDate and name
                {
                    if (textLines[noOfLines].task == job && textLines[noOfLines].endDate == searchEndDate)
                        searchedStrings.Add(noOfLines);
                }
                else//Any task with same start and end dates and name
                {
                    if (textLines[noOfLines].task == job && textLines[noOfLines].endDate >= searchStartDate && textLines[noOfLines].endDate <= searchEndDate)
                        searchedStrings.Add(noOfLines);
                }
            }
            return searchedStrings;
        }
        #endregion

        #region Database interactions
        private bool writeTasks(List<Task> task)
        {
            string textToWrite = null;
            foreach (Task eachTask in task)
                textToWrite += eachTask.print();
            try
            {
                File.WriteAllText(fileLoc, textToWrite);
            }
            catch
            {
                return false;
            }
            return true;
        }
        private List<Task> readTasks()
        {
            List<Task> allTasks = new List<Task>();

            try
            {
                // Read in every line in the file.
                using (StreamReader reader = new StreamReader(fileLoc))
                {
                    string line;
                    while ((line = reader.ReadLine()) != null)
                    {
                        // Do something with line
                        string[] split = line.Split('|');

                        Task task = new Task();
                        task.startDate = new DateTime(Convert.ToInt64(split[1]));
                        task.endDate = new DateTime(Convert.ToInt64(split[2]));
                        task.type = split[3];
                        task.status = split[4];
                        task.task = split[0];

                        allTasks.Add(task);
                    }
                }
            }
            catch (Exception ex)
            {
                allTasks.Add(new Task(ex.ToString(), DateTime.MinValue, DateTime.MinValue, GeneralConstant.KEY_ERROR));
            }
            return allTasks;
        }
        #endregion

        #region Existance Checker
        private bool isFolderExist(string path)
        {
            if (Directory.Exists(path))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        private bool isFileExist(string location)
        {
            if (File.Exists(location))
            {
                return true;
            }
            return false;
        }
        #endregion

        #region Value check
        private string valueChanged(string oldValue, string newValue)
        {
            if (newValue == null)
                return oldValue;
            return newValue;
        }

        private bool listContainTask(Task ttc, List<Task> lt)
        {
            foreach (Task t in lt)
            {
                if (t.task == ttc.task && t.startDate == ttc.startDate && t.endDate == ttc.endDate && t.type == ttc.type)
                    return true;
            }
            return false;
        }

        private bool isTheSameDay(DateTime date1, DateTime date2)
        {
            bool returnValue = (date1.Year == date2.Year && date1.DayOfYear == date2.DayOfYear);
            return returnValue;
        }
        #endregion
    }
}
